import mongoose from 'mongoose';
import { Session, ISession, ISessionDocument } from '../models/session';

/**
 * 会话数据访问对象
 * 提供会话相关的数据库操作方法
 */
export class SessionDAO {
  /**
   * 创建新会话
   * @param sessionData 会话数据
   * @returns 创建的会话文档
   */
  async create(sessionData: Partial<ISession>): Promise<ISessionDocument> {
    try {
      const session = new Session(sessionData);
      return await session.save();
    } catch (error: any) {
      if (error.code === 11000) {
        // 处理唯一性约束错误
        const field = Object.keys(error.keyPattern)[0];
        throw new Error(`${field} already exists`);
      }
      throw error;
    }
  }

  /**
   * 根据会话ID查找会话
   * @param sessionId 会话ID
   * @returns 会话文档或null
   */
  async findBySessionId(sessionId: string): Promise<ISessionDocument | null> {
    try {
      if (!sessionId || typeof sessionId !== 'string') {
        throw new Error('Invalid session ID format');
      }
      return await Session.findBySessionId(sessionId);
    } catch (error) {
      throw error;
    }
  }

  /**
   * 根据用户ID查找活跃会话
   * @param userId 用户ID
   * @returns 会话文档数组
   */
  async findByUserId(userId: mongoose.Types.ObjectId | string): Promise<ISessionDocument[]> {
    try {
      if (!mongoose.Types.ObjectId.isValid(userId)) {
        throw new Error('Invalid user ID format');
      }
      return await Session.findActiveByUserId(userId as mongoose.Types.ObjectId);
    } catch (error) {
      throw error;
    }
  }

  /**
   * 更新会话信息
   * @param sessionId 会话ID
   * @param updateData 更新数据
   * @returns 更新后的会话文档或null
   */
  async update(
    sessionId: string,
    updateData: Partial<ISession>
  ): Promise<ISessionDocument | null> {
    try {
      if (!sessionId || typeof sessionId !== 'string') {
        throw new Error('Invalid session ID format');
      }

      // 移除不应该被更新的字段
      const { _id, createdAt, sessionId: sid, ...safeUpdateData } = updateData as any;
      
      // 设置更新时间
      safeUpdateData.updatedAt = new Date();
      safeUpdateData.lastAccessTime = new Date();

      const updatedSession = await Session.findOneAndUpdate(
        { sessionId, isActive: true },
        safeUpdateData,
        { 
          new: true, 
          runValidators: true,
          lean: false
        }
      ).exec();

      return updatedSession;
    } catch (error: any) {
      if (error.code === 11000) {
        // 处理唯一性约束错误
        const field = Object.keys(error.keyPattern)[0];
        throw new Error(`${field} already exists`);
      }
      throw error;
    }
  }

  /**
   * 删除会话（软删除，设置为非活跃状态）
   * @param sessionId 会话ID
   * @returns 是否删除成功
   */
  async delete(sessionId: string): Promise<boolean> {
    try {
      if (!sessionId || typeof sessionId !== 'string') {
        throw new Error('Invalid session ID format');
      }

      const result = await Session.findOneAndUpdate(
        { sessionId },
        { isActive: false, updatedAt: new Date() },
        { new: true }
      ).exec();

      return result !== null;
    } catch (error) {
      throw error;
    }
  }

  /**
   * 删除过期会话
   * @returns 删除的会话数量
   */
  async deleteExpired(): Promise<number> {
    try {
      return await Session.cleanExpiredSessions();
    } catch (error) {
      throw error;
    }
  }

  /**
   * 根据用户ID查找主会话
   * @param userId 用户ID
   * @returns 主会话文档或null
   */
  async findMasterByUserId(userId: mongoose.Types.ObjectId | string): Promise<ISessionDocument | null> {
    try {
      if (!mongoose.Types.ObjectId.isValid(userId)) {
        throw new Error('Invalid user ID format');
      }
      return await Session.findMasterByUserId(userId as mongoose.Types.ObjectId);
    } catch (error) {
      throw error;
    }
  }

  /**
   * 根据父会话ID查找子会话
   * @param parentSessionId 父会话ID
   * @returns 子会话文档数组
   */
  async findSlavesByParentId(parentSessionId: string): Promise<ISessionDocument[]> {
    try {
      if (!parentSessionId || typeof parentSessionId !== 'string') {
        throw new Error('Invalid parent session ID format');
      }
      return await Session.findSlavesByParentId(parentSessionId);
    } catch (error) {
      throw error;
    }
  }

  /**
   * 根据系统ID查找会话
   * @param systemId 系统ID
   * @returns 会话文档数组
   */
  async findBySystemId(systemId: mongoose.Types.ObjectId | string): Promise<ISessionDocument[]> {
    try {
      if (!mongoose.Types.ObjectId.isValid(systemId)) {
        throw new Error('Invalid system ID format');
      }
      return await Session.findBySystemId(systemId as mongoose.Types.ObjectId);
    } catch (error) {
      throw error;
    }
  }

  /**
   * 使用户的所有会话失效
   * @param userId 用户ID
   * @returns 失效的会话数量
   */
  async invalidateUserSessions(userId: mongoose.Types.ObjectId | string): Promise<number> {
    try {
      if (!mongoose.Types.ObjectId.isValid(userId)) {
        throw new Error('Invalid user ID format');
      }
      return await Session.invalidateUserSessions(userId as mongoose.Types.ObjectId);
    } catch (error) {
      throw error;
    }
  }

  /**
   * 更新会话的最后访问时间
   * @param sessionId 会话ID
   * @returns 更新后的会话文档或null
   */
  async updateLastAccess(sessionId: string): Promise<ISessionDocument | null> {
    try {
      if (!sessionId || typeof sessionId !== 'string') {
        throw new Error('Invalid session ID format');
      }

      const session = await Session.findOne({ sessionId, isActive: true }).exec();
      if (!session) {
        return null;
      }

      return await session.updateLastAccess();
    } catch (error) {
      throw error;
    }
  }

  /**
   * 检查会话是否过期
   * @param sessionId 会话ID
   * @returns 是否过期
   */
  async isSessionExpired(sessionId: string): Promise<boolean> {
    try {
      if (!sessionId || typeof sessionId !== 'string') {
        throw new Error('Invalid session ID format');
      }

      const session = await Session.findOne({ sessionId }).exec();
      if (!session) {
        return true; // 会话不存在视为过期
      }

      return session.isExpired();
    } catch (error) {
      throw error;
    }
  }

  /**
   * 添加同步的子系统
   * @param sessionId 会话ID
   * @param systemId 系统ID
   * @returns 更新后的会话文档或null
   */
  async addSyncedSystem(
    sessionId: string, 
    systemId: mongoose.Types.ObjectId | string
  ): Promise<ISessionDocument | null> {
    try {
      if (!sessionId || typeof sessionId !== 'string') {
        throw new Error('Invalid session ID format');
      }
      if (!mongoose.Types.ObjectId.isValid(systemId)) {
        throw new Error('Invalid system ID format');
      }

      const session = await Session.findOne({ sessionId, isActive: true }).exec();
      if (!session) {
        return null;
      }

      return await session.addSyncedSystem(systemId as mongoose.Types.ObjectId);
    } catch (error) {
      throw error;
    }
  }

  /**
   * 移除同步的子系统
   * @param sessionId 会话ID
   * @param systemId 系统ID
   * @returns 更新后的会话文档或null
   */
  async removeSyncedSystem(
    sessionId: string, 
    systemId: mongoose.Types.ObjectId | string
  ): Promise<ISessionDocument | null> {
    try {
      if (!sessionId || typeof sessionId !== 'string') {
        throw new Error('Invalid session ID format');
      }
      if (!mongoose.Types.ObjectId.isValid(systemId)) {
        throw new Error('Invalid system ID format');
      }

      const session = await Session.findOne({ sessionId, isActive: true }).exec();
      if (!session) {
        return null;
      }

      return await session.removeSyncedSystem(systemId as mongoose.Types.ObjectId);
    } catch (error) {
      throw error;
    }
  }

  /**
   * 批量创建会话
   * @param sessionsData 会话数据数组
   * @returns 创建的会话文档数组
   */
  async batchCreate(sessionsData: Partial<ISession>[]): Promise<ISessionDocument[]> {
    try {
      if (!Array.isArray(sessionsData) || sessionsData.length === 0) {
        throw new Error('Sessions data must be a non-empty array');
      }

      // 验证每个会话数据
      for (const sessionData of sessionsData) {
        if (!sessionData.sessionId || !sessionData.userId || !sessionData.ipAddress) {
          throw new Error('Each session must have sessionId, userId, and ipAddress');
        }
      }

      const sessions = await Session.insertMany(sessionsData, { ordered: false });
      return sessions;
    } catch (error: any) {
      if (error.code === 11000) {
        throw new Error('One or more sessions already exist');
      }
      throw error;
    }
  }

  /**
   * 获取所有会话（支持分页和筛选）
   * @param criteria 筛选条件
   * @param options 分页和排序选项
   * @returns 会话列表和分页信息
   */
  async findAll(
    criteria: {
      userId?: mongoose.Types.ObjectId | string;
      sessionType?: 'master' | 'slave';
      isActive?: boolean;
      systemId?: mongoose.Types.ObjectId | string;
      parentSessionId?: string;
    } = {},
    options: {
      page?: number;
      limit?: number;
      sort?: string;
      select?: string;
    } = {}
  ): Promise<{ sessions: ISessionDocument[]; total: number; page: number; totalPages: number }> {
    try {
      const {
        page = 1,
        limit = 10,
        sort = '-lastAccessTime',
        select = ''
      } = options;

      // 构建查询条件
      const query: any = {};
      
      if (criteria.userId) {
        if (!mongoose.Types.ObjectId.isValid(criteria.userId)) {
          throw new Error('Invalid user ID format');
        }
        query.userId = criteria.userId;
      }
      
      if (criteria.sessionType) {
        query.sessionType = criteria.sessionType;
      }
      
      if (criteria.isActive !== undefined) {
        query.isActive = criteria.isActive;
      }
      
      if (criteria.systemId) {
        if (!mongoose.Types.ObjectId.isValid(criteria.systemId)) {
          throw new Error('Invalid system ID format');
        }
        query.systemId = criteria.systemId;
      }
      
      if (criteria.parentSessionId) {
        query.parentSessionId = criteria.parentSessionId;
      }

      // 计算跳过的文档数
      const skip = (page - 1) * limit;

      // 执行查询
      const [sessions, total] = await Promise.all([
        Session.find(query)
          .sort(sort)
          .skip(skip)
          .limit(limit)
          .select(select)
          .populate('userId', 'username email realName')
          .populate('systemId', 'name code')
          .exec(),
        Session.countDocuments(query).exec()
      ]);

      // 计算总页数
      const totalPages = Math.ceil(total / limit);

      return {
        sessions,
        total,
        page,
        totalPages
      };
    } catch (error) {
      throw error;
    }
  }
}

export default SessionDAO;